package com.zx.zxpicture.application.service.impl;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zx.zxpicture.infrstructure.common.ErrorCode;
import com.zx.zxpicture.infrstructure.exception.BusinessException;
import com.zx.zxpicture.infrstructure.exception.ThrowUtils;
import com.zx.zxpicture.infrstructure.mapper.PictureMapper;
import com.zx.zxpicture.interfaces.dto.spaceAnalyze.*;
import com.zx.zxpicture.interfaces.vo.vo.*;
import com.zx.zxpicture.domain.picture.entity.Picture;
import com.zx.zxpicture.domain.space.entity.Space;
import com.zx.zxpicture.domain.user.entity.User;
import com.zx.zxpicture.application.service.PictureApplicationService;
import com.zx.zxpicture.application.service.SpaceAnalyzeApplicationService;
import com.zx.zxpicture.application.service.SpaceApplicationService;
import com.zx.zxpicture.application.service.UserApplicationService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SpaceAnalyzeApplicationServiceImpl implements SpaceAnalyzeApplicationService {
    @Resource
    private UserApplicationService userApplicationService;
    @Resource
    private SpaceApplicationService spaceApplicationService;
    @Resource
    private PictureApplicationService pictureApplicationService;

    /**
     * 分析空间内存占比
     * @param spaceUsageAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public SpaceUsageAnalyzeResponse analyzeSpaceUsage(SpaceUsageAnalyzeRequest spaceUsageAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceUsageAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        //空间分析权限校验
        checkSpaceAnalyzeAuth(spaceUsageAnalyzeRequest,loginUser);
        if(spaceUsageAnalyzeRequest.isQueryAll() || spaceUsageAnalyzeRequest.isQueryPublic()){
            //查询公共图库或查询所有空间
            QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
            //构造查询条件
            queryWrapper.select("picSize");
            if(spaceUsageAnalyzeRequest.isQueryPublic()){
                //查询公共图库
                queryWrapper.isNull("spaceId");
            }
            //返回的数据只有picSize这一列，但是我只用picture对象来接收的话picture对象中只有picSize这个属性有值
            //其他属性全为空，会浪费内存，所以不使用对象封装，直接用Long来接收
            List<Object> objects = pictureApplicationService.getBaseMapper().selectObjs(queryWrapper);
            long usedSize = objects.stream().mapToLong(obj -> (Long) obj).sum();
            long usedCount = objects.size();
            //封装返回结果
            SpaceUsageAnalyzeResponse spaceUsageAnalyzeResponse = new SpaceUsageAnalyzeResponse();
            spaceUsageAnalyzeResponse.setUsedSize(usedSize);
            spaceUsageAnalyzeResponse.setUsedCount(usedCount);
            // 公共图库无上限、无比例
            spaceUsageAnalyzeResponse.setMaxSize(null);
            spaceUsageAnalyzeResponse.setSizeUsageRatio(null);
            spaceUsageAnalyzeResponse.setMaxCount(null);
            spaceUsageAnalyzeResponse.setCountUsageRatio(null);
            return spaceUsageAnalyzeResponse;
        }else{
            //查询指定空间
            Long spaceId = spaceUsageAnalyzeRequest.getSpaceId();
            //获取空间信息
            Space space = spaceApplicationService.getById(spaceId);
            // 构造返回结果
            SpaceUsageAnalyzeResponse response = new SpaceUsageAnalyzeResponse();
            response.setUsedSize(space.getTotalSize());
            response.setMaxSize(space.getMaxSize());
            // 后端直接算好百分比，这样前端可以直接展示
            //NumberUtil.round()hutool包的四舍五入方法
            //乘以xx.0是为了防止精度丢失，乘以100是为了直接获得百分比
            double sizeUsageRatio = NumberUtil.round(space.getTotalSize() * 100.0 / space.getMaxSize(), 2).doubleValue();
            response.setSizeUsageRatio(sizeUsageRatio);
            response.setUsedCount(space.getTotalCount());
            response.setMaxCount(space.getMaxCount());
            double countUsageRatio = NumberUtil.round(space.getTotalCount() * 100.0 / space.getMaxCount(), 2).doubleValue();
            response.setCountUsageRatio(countUsageRatio);
            return response;
        }
    }

    /**
     * 空间分类图片分析
     * @param spaceCategoryAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceCategoryAnalyzeResponse> analyzeSpaceCategory(SpaceCategoryAnalyzeRequest spaceCategoryAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceCategoryAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        //校验空间分析权限
        checkSpaceAnalyzeAuth(spaceCategoryAnalyzeRequest,loginUser);
        //填充分析范围查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        fillAnalyzeQueryWrapper(spaceCategoryAnalyzeRequest,queryWrapper);
        //查询
        queryWrapper.select("category",
                "count(*) as count",
                "sum(picSize) as totalSize")
                .groupBy("category");
        //优化代码，直接接收查询返回的数据，不封装到picture对象中，避免过多空字段占用内存
        //一条数据就是一个Map 例如:{ "category":"美女", "count": 2, "totalSize": 1024 }
        List<Map<String, Object>> maps = pictureApplicationService.getBaseMapper().selectMaps(queryWrapper);
        //Map转换为SpaceCategoryAnalyzeResponse
        return maps.stream().map(map -> {
            String category = (String) map.get("category");
            Long count = ((Number)map.get("count")).longValue();
            Long totalSize = ((Number)map.get("totalSize")).longValue();
            return new SpaceCategoryAnalyzeResponse(category, count, totalSize);
        }).collect(Collectors.toList());
    }

    /**
     * 空间图片标签分析
     * @param spaceTagAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceTagAnalyzeResponse> analyzeSpaceTag(SpaceTagAnalyzeRequest spaceTagAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceTagAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        //校验空间分析权限
        checkSpaceAnalyzeAuth(spaceTagAnalyzeRequest,loginUser);
        //构造条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        //填充分析范围查询条件
        fillAnalyzeQueryWrapper(spaceTagAnalyzeRequest,queryWrapper);
        queryWrapper.select("tags");
        List<Object> tagsJsonStrList = pictureApplicationService.getBaseMapper().selectObjs(queryWrapper);
        //集合中的元素就是标签JSON数组
        List<String> tagsJsonList = tagsJsonStrList.stream()
                .filter(ObjectUtil::isNotEmpty)
                .map(obj -> (String) obj)
                .collect(Collectors.toList());
        //使用flatMap将集合中的所有元素都平铺开
        Map<String, Long> map = tagsJsonList.stream()
                //将JSON数组转换为集合，再将集合中的数据平铺出来
                .flatMap(tagsJson -> JSONUtil.toList(tagsJson, String.class).stream())
                //自定义key和value
                .collect(Collectors.groupingBy(tag -> tag, Collectors.counting()));
        //转换类型
        return map.entrySet().stream()
                //sorted比较规则是结果为正数，表示e2的值大于e1的值，则e2排在前面，反之e1排在前面
                //Long.compare(e1,e2)排序规则是如果e1>e2，则返回正数，如果e1<e2，则返回负数，如果e1=e2，则返回0
                //当前是降序
                .sorted((e1,e2) -> Long.compare(e2.getValue(), e1.getValue()))
                .map(entry -> new SpaceTagAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 分析空间图片大小
     * @param spaceSizeAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceSizeAnalyzeResponse> analyzeSpacePicSize(SpaceSizeAnalyzeRequest spaceSizeAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceSizeAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        //校验空间分析权限
        checkSpaceAnalyzeAuth(spaceSizeAnalyzeRequest,loginUser);
        //构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        //填充分析范围查询条件
        fillAnalyzeQueryWrapper(spaceSizeAnalyzeRequest,queryWrapper);
        queryWrapper.select("picSize");
        List<Long> list = pictureApplicationService.getBaseMapper().selectObjs(queryWrapper)
                .stream()
                .map(obj -> ((Number) obj).longValue())
                .collect(Collectors.toList());
        // 定义分段范围，注意使用有序 Map
        Map<String, Long> sizeRanges = new LinkedHashMap<>();
        // 定义哈希表
        sizeRanges.put("<100KB", 0L);
        sizeRanges.put("100KB-500KB", 0L);
        sizeRanges.put("500KB-1MB", 0L);
        sizeRanges.put(">1MB", 0L);

        // 单次遍历
        for (Long size : list) {
            if (size < 100 * 1024) {
                sizeRanges.put("<100KB", sizeRanges.get("<100KB") + 1);
            } else if (size < 500 * 1024) {
                sizeRanges.put("100KB-500KB", sizeRanges.get("100KB-500KB") + 1);
            } else if (size < 1 * 1024 * 1024) {
                sizeRanges.put("500KB-1MB", sizeRanges.get("500KB-1MB") + 1);
            } else {
                sizeRanges.put(">1MB", sizeRanges.get(">1MB") + 1);
            }
        }
        // 转换为响应对象
        return sizeRanges.entrySet().stream()
                .map(entry -> new SpaceSizeAnalyzeResponse(entry.getKey(), entry.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * 用户行为分析
     * @param spaceUserAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<SpaceUserAnalyzeResponse> analyzeUser(SpaceUserAnalyzeRequest spaceUserAnalyzeRequest, User loginUser) {
        ThrowUtils.throwIf(spaceUserAnalyzeRequest == null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(spaceUserAnalyzeRequest.getUserId() == null || spaceUserAnalyzeRequest.getUserId() <= 0, ErrorCode.PARAMS_ERROR,"请输入正确的用户id");
        //校验空间分析权限
        checkSpaceAnalyzeAuth(spaceUserAnalyzeRequest,loginUser);
        //构造查询条件
        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
       queryWrapper.eq("userId",spaceUserAnalyzeRequest.getUserId());
       fillAnalyzeQueryWrapper(spaceUserAnalyzeRequest,queryWrapper);
        // 分析维度：每日、每周、每月
        String timeDimension = spaceUserAnalyzeRequest.getTimeDimension();
        switch (timeDimension) {
            case "day":
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m-%d') AS period", "COUNT(*) AS count");
                break;
            case "week":
                queryWrapper.select("YEARWEEK(createTime) AS period", "COUNT(*) AS count");
                break;
            case "month":
                queryWrapper.select("DATE_FORMAT(createTime, '%Y-%m') AS period", "COUNT(*) AS count");
                break;
            default:
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的时间维度");
        }
        queryWrapper.groupBy("period").orderByAsc("period");
        List<Map<String, Object>> maps = pictureApplicationService.getBaseMapper().selectMaps(queryWrapper);
         return maps.stream()
                .map(obj -> {
                    String period = obj.get("period").toString();
                    long count = ((Number) obj.get("count")).longValue();
                    return new SpaceUserAnalyzeResponse(period, count);
                }).collect(Collectors.toList());
    }

    /**
     * 空间使用分析
     * 管理员使用的
     * @param spaceRankAnalyzeRequest
     * @param loginUser
     * @return
     */
    @Override
    public List<Space> getSpaceRankAnalyze(SpaceRankAnalyzeRequest spaceRankAnalyzeRequest, User loginUser) {
        // 仅管理员可查看空间排行
        ThrowUtils.throwIf(!userApplicationService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR, "无权查看空间排行");
        //构造查询条件
        QueryWrapper<Space> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id","spaceName","totalSize","userId")
                .orderByDesc("totalSize")
                //limit 10
                .last("LIMIT "+spaceRankAnalyzeRequest.getTopN());
        return spaceApplicationService.list(queryWrapper);
    }

    /**
     * 校验空间分析权限
     * @param spaceAnalyzeRequest
     * @param loginUser
     */
    private void checkSpaceAnalyzeAuth(SpaceAnalyzeRequest spaceAnalyzeRequest, User loginUser) {
        // 检查权限
        if (spaceAnalyzeRequest.isQueryAll() || spaceAnalyzeRequest.isQueryPublic()) {
            // 全空间分析或者公共图库权限校验：仅管理员可访问
            ThrowUtils.throwIf(!userApplicationService.isAdmin(loginUser), ErrorCode.NO_AUTH_ERROR, "无权访问公共图库");
        } else {
            // 私有空间权限校验
            Long spaceId = spaceAnalyzeRequest.getSpaceId();
            ThrowUtils.throwIf(spaceId == null || spaceId <= 0, ErrorCode.PARAMS_ERROR);
            Space space = spaceApplicationService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_FOUND_ERROR, "空间不存在");
            //空间管理员和管理员才允许访问
            spaceApplicationService.checkSpaceAuth(loginUser, space);
        }
    }


    /**
     * 自动填充查询范围
     * @param spaceAnalyzeRequest
     * @param queryWrapper
     */
    private static void fillAnalyzeQueryWrapper(SpaceAnalyzeRequest spaceAnalyzeRequest, QueryWrapper<Picture> queryWrapper) {
        if (spaceAnalyzeRequest.isQueryAll()) {
            return;
        }
        if (spaceAnalyzeRequest.isQueryPublic()) {
            queryWrapper.isNull("spaceId");
            return;
        }
        Long spaceId = spaceAnalyzeRequest.getSpaceId();
        if (spaceId != null) {
            queryWrapper.eq("spaceId", spaceId);
            return;
        }
        throw new BusinessException(ErrorCode.PARAMS_ERROR, "未指定查询范围");
    }

}
